﻿using System;
using System.Linq.Expressions;
using System.Reflection;

namespace BSU.GraduateWork.Utilities
{
    public static class Reflector
    {
        public static String GetPropertyName<T>(Expression<Func<T, object>> property)
        {
            UnaryExpression unaryExpression = property.Body as UnaryExpression;
            MemberExpression memberExpression =
                (MemberExpression)(unaryExpression != null ? unaryExpression.Operand : property.Body);
            return memberExpression.Member.Name;
        }

        public static String GetFullPropertyName<T>(Expression<Func<T, object>> property)
        {
            UnaryExpression unaryExpression = property.Body as UnaryExpression;
            MemberExpression memberExpression =
                (unaryExpression != null ? unaryExpression.Operand : property.Body) as MemberExpression;
            if (memberExpression == null || memberExpression.NodeType == ExpressionType.Parameter)
            {
                return String.Empty;
            }
            else
            {
                var expression = Expression.Lambda<Func<T, object>>(memberExpression.Expression, Expression.Parameter(typeof(T), String.Empty));
                string basePropertyName = GetFullPropertyName(expression);
                if (String.IsNullOrEmpty(basePropertyName))
                {
                    return memberExpression.Member.Name;
                }
                return String.Format("{0}.{1}", basePropertyName, memberExpression.Member.Name);
            }
        }

        public static String GetMethodName<T>(Expression<Func<T, object>> method)
        {
            MethodCallExpression methodCall = method.Body as MethodCallExpression;
            if (methodCall == null)
            {
                throw new ArgumentException();
            }
            return methodCall.Method.Name;
        }

        public static Expression<Func<T, Tkey>> GetPropertyLambda<T, Tkey>(string propertyName)
        {
            if (String.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName", "propertyName can not be null or empty.");
            }
            var sortParts = propertyName.Split('.');
            var param = Expression.Parameter(typeof(T), String.Empty);
            var property = Expression.Property(param, sortParts[0]);
            Type propertyType = GetPropertyType<T>(propertyName);

            for (int i = 1; i < sortParts.Length; i++)
            {
                property = Expression.Property(property, sortParts[i]);
            }

            return Expression.Lambda<Func<T, Tkey>>(Expression.Convert(property, propertyType), param);
        }

        public static Type GetPropertyType<T>(string propertyName)
        {
            if (String.IsNullOrEmpty(propertyName))
            {
                throw new ArgumentNullException("propertyName", "propertyName can not be null or empty.");
            }
            var sortParts = propertyName.Split('.');
            PropertyInfo propertyInfo = typeof(T).GetProperty(sortParts[0]);
            if (propertyInfo == null)
            {
                throw new ArgumentException("Property doesn't exist.", "propertyName");
            }
            Type propertyType = propertyInfo.PropertyType;

            for (int i = 1; i < sortParts.Length; i++)
            {
                propertyInfo = propertyType.GetProperty(sortParts[i]);
                if (propertyInfo == null)
                {
                    throw new ArgumentException("Property doesn't exist.", "propertyName");
                }
                propertyType = propertyInfo.PropertyType;

            }
            return propertyType;
        }
    }
}
